<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <link rel="icon" href="{{ url_prefix }}/img/favicon.png">
        <title>{{ site_id }} | Rhasspy</title>

        <!-- CSS -->
        <link rel="stylesheet" href="{{ url_prefix }}/css/bootstrap.min.css">
        <link rel="stylesheet" href="{{ url_prefix }}/css/fontawesome-all.min.css">
        <link rel="stylesheet" href="{{ url_prefix }}/css/main.css">

        <style>
         [x-cloak] { display: none; }
        </style>
    </head>

    <body x-data="rhasspy">
        <noscript>
            <strong>Please enable Javascript to continue.</strong>
        </noscript>

        <!-- Top Bar -->
        <nav class="navbar navbar-expand-sm navbar-dark bg-dark fixed-top">

            <!-- Logo -->
            <a href="{{ url_prefix }}/">
                <img id="logo" title="Rhasspy" class="navbar-brand" src="{{ url_prefix }}/img/logo.png">
            </a>

            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navbarSupportedContent">

                <!-- Version -->
                <div class="navbar-container">
                    <a href="{{ url_prefix }}/openapi/" title="HTTP API" class="badge badge-info">{{ version }}</a>
                </div>

                <!-- Page Drop Down -->
                <div class="navbar-container ml-3">
                    <div class="nav-item dropdown">
                        <a class="btn btn-outline-light  dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            {{ page }}
                        </a>
                        <div class="dropdown-menu" aria-labelledby="navbarDropdown">
                            <a class="dropdown-item" href="{{ url_prefix }}/">
                                <i class="fas fa-home mr-2"></i>Home
                            </a>
                            <a class="dropdown-item" href="{{ url_prefix }}/sentences">
                                <i class="fas fa-align-left mr-2"></i>Sentences
                            </a>
                            <a class="dropdown-item" href="{{ url_prefix }}/slots">
                                <i class="fas fa-dot-circle mr-2"></i>Slots
                            </a>
                            <a class="dropdown-item" href="{{ url_prefix }}/words">
                                <i class="fas fa-quote-right mr-2"></i>Words
                            </a>
                            <a class="dropdown-item" href="{{ url_prefix }}/settings">
                                <i class="fas fa-cogs mr-2"></i>Settings
                            </a>
                            <div class="dropdown-divider"></div>
                            <a class="dropdown-item" href="{{ url_prefix }}/advanced">
                                <i class="fas fa-skull-crossbones mr-2"></i>Advanced
                            </a>
                            <div class="dropdown-divider"></div>
                            <a class="dropdown-item" href="{{ url_prefix }}/docs/" target="_blank">
                                <i class="fas fa-book mr-2"></i>Documentation
                                <i class="fas fa-external-link-alt ml-1"></i>
                            </a>
                        </div>
                    </div>
                </div>

                <!-- Log Button -->
                <button onclick="$('#logModal').modal('show')" class="btn btn-secondary ml-2" title="Show Rhasspy log">Log</button>

                <!-- Top Bar Buttons -->
                <div class="navbar-container ml-auto">
                    <span title="Profile name" class="badge badge-primary ml-2" style="font-size: 1em" x-text="profile.name"></span>

                    <button onclick="rhasspy.train()" class="btn btn-success ml-2" title="Train profile">Train</button>
                    <button onclick="rhasspy.restart()" class="btn btn-danger ml-2" title="Restart Rhasspy">Restart</button>

                    {% if show_system_button: %}
                    <button onclick="powerOff()" class="btn btn-secondary ml-2" title="Reboot/Shutdown System">
                        <i class="fas fa-lg fa-inverse fa-power-off" ></i>
                    </button>
                    {% endif %}
                </div>
            </div>
        </nav>

        <!-- Main -->
        <div>
            <div class="row">
                <div class="col-xs-auto bg-dark p-0">
                    <a href="{{ url_prefix }}/" title="Home" class="btn w-100  {{ "btn-light rounded-0" if page == "Home" else "btn-dark" }}">
                        <i class="fas fa-home fa-lg ml-2"></i>
                    </a>
                    <br>
                    <a href="{{ url_prefix }}/sentences" title="Sentences" class="btn w-100 {{ "btn-light rounded-0" if page == "Sentences" else "btn-dark" }}">
                        <i class="fas fa-align-left fa-lg ml-2"></i>
                    </a>
                    <br>
                    <a href="{{ url_prefix }}/slots" title="Slots" class="btn w-100 {{ "btn-light rounded-0" if page == "Slots" else "btn-dark" }}">
                        <i class="fas fa-dot-circle fa-lg ml-2"></i>
                    </a>
                    <br>
                    <a href="{{ url_prefix }}/words" title="Words" class="btn w-100 {{ "btn-light rounded-0" if page == "Words" else "btn-dark" }}">
                        <i class="fas fa-quote-right fa-lg ml-2"></i>
                    </a>
                    <br>
                    <a href="{{ url_prefix }}/settings" title="Settings" class="btn w-100 {{ "btn-light rounded-0" if page == "Settings" else "btn-dark" }}">
                        <i class="fas fa-cogs fa-lg ml-2"></i>
                    </a>
                    <br>
                    <a href="{{ url_prefix }}/docs/" target="_blank" title="Documentation" class="btn btn-info w-100 rounded-0">
                        <i class="fas fa-book fa-lg ml-2"></i>
                    </a>
                </div>
                <div class="col main-container">
                    <!-- New Alert -->
                    <div id="new-alert" class="alert alert-success alert-dismissable" role="alert" x-show="allDummy()" x-cloak>
                        <span class="ml-2">
                            New to Rhasspy? Try the <a target="_blank" href="{{ url_prefix }}/docs/tutorials/#getting-started-guide">Getting Started Guide</a> &#x263A;
                        </span>
                        <button type="button" onclick="$('#new-alert').hide()" class="close ml-auto" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <!-- End New Alert -->

                    <!-- Download Alert -->
                    <div id="download-alert" class="alert alert-warning alert-dismissable" role="alert" x-show="!$.isEmptyObject(missingFiles)" x-cloak>
                        <button onclick="rhasspy.downloadProfile()" class="btn btn-primary">Download</button>
                        <span class="ml-2">
                            Rhasspy needs to download some files for your profile <a href="#" onclick="showFilesToDownload()">(<span x-text="Object.keys(missingFiles).length"></span> missing, <span x-text="missingSize"></span>)</a>
                        </span>
                        <button type="button" onclick="$('#download-alert').hide()" class="close ml-auto" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <!-- End Download Alert -->

                    <!-- Unknown Words Alert -->
                    <div id="unknown-words-alert" class="alert alert-warning alert-dismissable" role="alert" x-show="!$.isEmptyObject(unknownWords)" x-cloak>
                        <a title="Go to Words page" href="{{ url_prefix }}/words" class="btn btn-primary">View</a>
                        <span class="ml-2">
                            There are <span x-text="Object.keys(unknownWords).length"></span> word(s) that Rhasspy isn't sure how to pronounce
                        </span>
                        <button type="button" onclick="$('#unknown-words-alert').hide()" class="close ml-auto" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <!-- End Unknown Words Alert -->

                    <div class="main-body">
                        {% block body %}{% endblock %}
                    </div>
                </div>
            </div>
        </div>
        <!-- End Main -->

        <!-- Busy Modal -->
        <div id="busyModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="busyModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 id="busyText" class="modal-title" id="busyModalLabel"></h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <div class="progress">
                            <div class="progress-bar progress-bar-striped progress-bar-animated w-100" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- End Busy Modal -->

        <!-- Download Modal -->
        <div id="downloadModal" class="modal  fade" tabindex="-1" role="dialog" aria-labelledby="downloadModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title">Downloading Profile Files</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <template x-for="fileKey in sortMissingFiles()" :key="fileKey">
                            <div class="row mt-2">
                                <div class="col" x-text="fileKey"></div>
                                <div class="col">
                                    <div class="progress">
                                        <div x-bind:id="'progress-download-' + missingFiles[fileKey].index" class="progress-bar progress-bar-striped progress-bar-animated w-0" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100"></div>
                                    </div>
                                </div>
                            </div>
                        </template>
                    </div>
                </div>
            </div>
        </div>
        <!-- End Download Modal -->

        <!-- Download Files Modal -->
        <div id="downloadFilesModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="downloadFilesModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="downloadFilesModalLabel">Files to Download</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <textarea id="downloadFiles" type="text" class="form-control" rows="15"></textarea>
                    </div>
                </div>
            </div>
        </div>
        <!-- End Download Files Modal -->

        <!-- Alert -->
        <div id="alert" style="display: none;" class="alert alert-success main-alert alert-dismissable" role="alert">
            <button @click="clearAlert()" type="button" class="close" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
            <span id="alertText"></span>
        </div>
        <!-- End Alert -->

        <!-- Log -->
        <div id="logModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="logModalLabel" aria-hidden="true">
            <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h5 class="modal-title" id="logModalLabel">Rhasspy Log</h5>

                        <button type="button" class="btn btn-primary ml-2" @click="log.text = ''">Clear</button>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        <div class="container">
                            <div class="text-muted pl-1">
                                <p>
                                    This log is streamed live from your Rhasspy server at <tt>ws://<span x-text="window.location.host"></span>{{ url_prefix }}/api/events/log</tt>
                                </p>
                            </div>
                            <textarea x-model="log.text" class="form-control" type="text" rows="20"></textarea>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- End Log -->

        <!-- Power Modal -->
        <div class="modal fade" id="power-modal" tabindex="-1" role="dialog" aria-labelledby="powerLabel" aria-hidden="true">
            <div class="modal-dialog modal-lg" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <h3 class="modal-title" id="powerLabel">System Power</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body container">
                        <div class="row justify-content-center">
                            <div class="col">
                                <button class="btn btn-warning power-btn" onclick="restartSystem()">Restart System</button>
                            </div>
                        </div>
                        <div class="row justify-content-center">
                            <div class="col">
                                <button class="btn btn-danger power-btn" onclick="shutdownSystem()">Shutdown System</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- End Power Modal -->

        <!-- Load Javascript libraries -->
        <script src="{{ url_prefix }}/js/reconnecting-websocket-iife.min.js"></script>
        <script src="{{ url_prefix }}/js/jquery-3.4.1.min.js"></script>
        <script src="{{ url_prefix }}/js/popper.min.js"></script>
        <script src="{{ url_prefix }}/js/bootstrap.min.js"></script>
        <script src="{{ url_prefix }}/js/alpine.js" defer></script>

        <script type="text/javascript">
         // Shared object with profile and functions
         var rhasspy = {

             // Profile object and non-default settings
             profile: {{ profile_json | safe }},
             localProfileJson: { value: '' },

             // Words with guessed pronunciations
             unknownWords: {},
             unknownWordsText: { value: '' },

             // Files that need to be downloaded
             missingFiles: {},
             missingSize: '',

             // Initial microphone, speech to text, etc. systems
             initialSystems: {
                 mqtt: {% if mqtt_external: %}true{% else: %}false{% endif %},
                 microphone: '{{ microphone_system }}',
                 wake: '{{ wake_system }}',
                 stt: '{{ speech_to_text_system }}',
                 intent: '{{ intent_system }}',
                 tts: '{{ text_to_speech_system }}',
                 sounds: '{{ sounds_system }}',
                 dialogue: '{{ dialogue_system }}',
                 handle: '{{ handle_system }}'
             },

             // Do API calls for generally needed bits of data
             init: () => {
                 // Load profile
                 $.get('{{ url_prefix }}/api/profile', (r) => {
                     // Update in-place to avoid breaking alpine js proxies
                     Object.assign(rhasspy.profile, r);

                     // Update systems
                     Object.assign(rhasspy.initialSystems, {
                         mqtt: r.mqtt.enabled,
                         microphone: r.microphone.system,
                         wake: r.wake.system,
                         stt: r.speech_to_text.system,
                         intent: r.intent.system,
                         tts: r.text_to_speech.system,
                         sounds: r.sounds.system,
                         dialogue: r.dialogue.system,
                         handle: r.handle.system
                     });
                 })

                 // Local profile
                 $.get('{{ url_prefix }}/api/profile?layers=profile', (r) => {
                     rhasspy.localProfileJson.value = JSON.stringify(r, null, 4);
                 })

                 // Load missing files
                 $.get('{{ url_prefix }}/api/profiles', (r) => {
                     rhasspy.missingSize = r.missing_size;
                     var missingFiles = r.missing_files;

                     Object.keys(missingFiles).forEach((k, i) => {
                         missingFiles[k]["index"] = i;
                     })

                    rhasspy.missingFiles = missingFiles;
                 })

                 // Load unknown words
                 $.get('{{ url_prefix }}/api/unknown-words', (r) => {
                     rhasspy.unknownWords = r;
                     rhasspy.unknownWordsText.value =
                         Object.entries(rhasspy.unknownWords)
                               .map((kv) => kv[0] + ' ' + kv[1])
                               .join('\n');
                 })
             },

             // True if the busy modal should be hidden after its finished with its show animation.
             busyHidden: true,

             // Show the busy modal and start the Rhasspy logo spinning
             showBusy: (message) => {
                 rhasspy.busyHidden = false;
                 $('#logo').addClass('spinner')
                 $('#busyText').html(message)
                 $('#busyModal').modal('show')
             },

             // Hide the busy modal and stop the Rhasspy logo from spinning
             hideBusy: (message) => {
                 rhasspy.busyHidden = true;
                 $('#logo').removeClass('spinner')
                 $('#busyModal').modal('hide')
             },

             // Display an alert message at the bottom of the screen for 20 seconds
             alert: (message, level) => {
                 rhasspy.clearAlert()

                 level = (level === undefined) ? 'info' : level;
                 $('#alert').addClass('alert-' + level)
                 $('#alertText').text(message)
                 $('#alert').show()

                 // Hide alert after 20 seconds
                 setTimeout(rhasspy.clearAlert, 20000)
             },

             // Hide the alert message
             clearAlert: () => {
                 $('#alert').removeClass('alert-info')
                 $('#alert').removeClass('alert-danger')
                 $('#alert').removeClass('alert-warning')
                 $('#alert').removeClass('alert-success')
                 $('#alert').hide()
             },

             // Display the busy modal with a message and do an AJAX POST.
             postBusy: (message, url, data, hideOnComplete, contentType) => {
                 return rhasspy.ajaxBusy('POST', message, url, data, hideOnComplete, contentType);
             },

             // Display the busy modal with a message and do an AJAX GET.
             getBusy: (message, url, data, hideOnComplete, contentType) => {
                 return rhasspy.ajaxBusy('GET', message, url, data, hideOnComplete, contentType);
             },

             // Display the busy modal with a message and do an AJAX call.
             // Adds URL prefix automatically.
             // Returns the AJAX request.
             ajaxBusy: (method, message, url, data, hideOnComplete, contentType) => {
                 hideOnComplete = (hideOnComplete === undefined) ? true : hideOnComplete;
                 contentType = (contentType === undefined) ? 'application/json' : contentType;

                 if (message != '') {
                    rhasspy.showBusy(message);
                 }
                 return $.ajax({
                     url: '{{ url_prefix }}' + url,
                     type: method,
                     data: data,
                     processData: false,
                     contentType: contentType,
                     complete: () => {
                         // Hide busy modal
                         if (hideOnComplete) {
                             rhasspy.hideBusy();
                         }
                     },
                     error: (r) => {
                         // Show error alert
                         rhasspy.alert(r.responseText || r.statusText || JSON.stringify(r), 'danger');
                     }
                 })
             },

             // POSTs to /api/train and displays a busy modal
             train: (hideOnComplete) => {
                 return rhasspy.postBusy('Training Profile', '/api/train', '', hideOnComplete)
                            .done((r) => {
                                rhasspy.alert(r, 'success');
                                rhasspy.init();
                            });
             },

             // POSTs to /api/restart and displays a busy modal
             restart: (hideOnComplete) => {
                 return rhasspy.postBusy('Restarting Rhasspy', '/api/restart', '', hideOnComplete)
                            .done((r) => {
                                rhasspy.alert(r.responseText || r, 'success');
                                rhasspy.init();
                            });
             },

             downloading: false,

             // Starts profile file downloads and monitors status
             downloadProfile: () => {
                 rhasspy.downloading = true;
                 $('#downloadModal').modal('show');

                 // Start monitoring download status
                 rhasspy.updateDownloadStatus()

                 // POST to /api/download-profile and show progress
                 $.ajax({
                     url: '{{ url_prefix }}/api/download-profile',
                     type: 'POST',
                     complete: () => {
                         rhasspy.downloading = false;

                         // Hide download dialog
                         $('#downloadModal').modal('hide')
                     },
                     success: () => {
                         // Re-train and restart when done downloading
                         rhasspy.train()
                                .done(function() {
                                    rhasspy.restart();
                                });
                     },
                     error: (error) => {
                         rhasspy.downloading = false;

                         // Show error alert
                         rhasspy.alert(error.responseText || error.statusText || error, 'danger');
                     }
                 })

             },

             // Polls /api/download-status for downloaded file statuses
             updateDownloadStatus: () => {
                 if (!rhasspy.downloading) {
                     return;
                 }

                 $.get('{{ url_prefix }}/api/download-status', (r) => {
                     Object.keys(r).forEach((fileKey) => {
                         var remoteFile = r[fileKey];
                         var localFile = rhasspy.missingFiles[fileKey];
                         var progressId = '#progress-download-' + localFile.index;

                         $(progressId).width(remoteFile.bytes_percent + '%');

                         if (remoteFile.done) {
                             $(progressId).removeClass('progress-bar-animated');
                             $(progressId).addClass('bg-success');
                         }
                     })
                 }).always(() => {
                     setTimeout(() => {
                         rhasspy.updateDownloadStatus();
                     }, 1000);
                 })
             },

             // Return missing file keys sorted by file size (descending)
             sortMissingFiles: () => {
                 return Object.keys(rhasspy.missingFiles).sort((a, b) => {
                     var aSize = rhasspy.missingFiles[a].bytes_expected || 0;
                     var bSize = rhasspy.missingFiles[b].bytes_expected || 0;
                     return bSize - aSize;
                 })
             },

             // Converts a Pocketsphinx threshold to a sensitivity value in [0, 1]
             thresholdToSensitivity: (threshold) => {
                 var high = 50
                 var low = 5

                 var exp = -Math.log10(threshold)
                 var s = (exp - low) / (high - low)
                 return Math.min(1, Math.max(0, s)).toFixed(1)
             },

             // Converts a a sensitivity value in [0, 1] to a Pocktsphinx threshold
             sensitivityToThreshold: (sensitivity) => {
                 var high = 50
                 var low = 5

                 var exp = (sensitivity * (high - low)) + low
                 return Math.pow(10, -exp)
             },

             // Computed getter for Pocketsphinx sensitivity
             get pocketsphinxWakeSensitivity() {
                 return this.thresholdToSensitivity(
                     this.profile.wake.pocketsphinx.threshold
                 );
             },

             // Computed setter for Pocketsphinx sensitivity
             set pocketsphinxWakeSensitivity(sensitivity) {
                 this.profile.wake.pocketsphinx.threshold =
                     this.sensitivityToThreshold(sensitivity);
             },

             // Creates a new websocket and the calls afterFunc with it.
             // Adds url prefix automatically.
             // Websocket will automatically reconnect after 5 seconds.
             makeWebSocket: (urlFragment, afterFunc) => {
                 // Use URL to decide websocket protocol
                 var wsProtocol = 'ws://';
                 if (window.location.protocol == 'https:') {
                     wsProtocol = 'wss://';
                 }

                 var wsURL = wsProtocol + window.location.host + '{{ url_prefix }}' + urlFragment;
                 var socket = new ReconnectingWebSocket(wsURL);

                 if (afterFunc != undefined) {
                     afterFunc(socket);
                 }

                 return socket;
             },

             // Log text stream from Rhasspy server.
             log: { text: '' }

         };

         // Automatically hide busy modal after show animation if busyHidden
         $('#busyModal').on('shown.bs.modal', () => {
             if (rhasspy.busyHidden) {
                 $('#busyModal').modal('hide');
             }
         })

         // Automatically hide download modal after show animation if not downloading
         $('#downloadModal').on('shown.bs.modal', () => {
             if (!rhasspy.downloading) {
                 $('#downloadModal').modal('hide');
             }
         })

         function showFilesToDownload() {
             $('#downloadFiles').val(JSON.stringify(rhasspy.missingFiles, null, 4));
             $('#downloadFilesModal').modal('show');
         }

         function allDummy() {
             var systems = ['microphone', 'handle', 'wake', 'speech_to_text',
                            'intent', 'text_to_speech', 'sounds', 'dialogue'];

             for (system of systems) {
                 if (rhasspy.profile[system]['system'] != 'dummy') {
                     return false;
                 }
             }

             return true;
         }

         function powerOff() {
             $('#power-modal').modal();
         }

         function restartSystem() {
             $('#power-modal').modal('hide');
             rhasspy.postBusy('Restarting System', '/api/system', 'reboot');
         }

         function shutdownSystem() {
             $('#power-modal').modal('hide');
             rhasspy.postBusy('Restarting System', '/api/system', 'shutdown');
         }

         $(window).on("load", () => {
             // Disable GET caches
             $.ajaxSetup({ cache: false });

             rhasspy.init();

             // Connect log websocket on startup
             rhasspy.makeWebSocket(
                 '/api/events/log',
                 (socket) => {
                     socket.onmessage = (e) => {
                         rhasspy.log.text = e.data + '\n' + rhasspy.log.text;
                     }
                 });
         })

        </script>

        {% block script %}{% endblock %}
    </body>
</html>
